[id].vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <div class="admin--page-content">
  3. <div class="admin--form">
  4. <table class="admin--form--table">
  5. <colgroup>
  6. <col style="width: 140px;">
  7. <col>
  8. </colgroup>
  9. <tbody>
  10. <tr>
  11. <th><div>아이템명</div></th>
  12. <td class="admin--table-title">{{ data.name || "-" }}</td>
  13. </tr>
  14. <tr>
  15. <th><div>구분</div></th>
  16. <td>
  17. <span :class="['admin--badge', typeBadgeClass(data.type)]">{{ typeLabel(data.type) }}</span>
  18. </td>
  19. </tr>
  20. <tr v-if="data.type !== 'T'">
  21. <th><div>포인트</div></th>
  22. <td>{{ data.point !== null && data.point !== undefined ? data.point + "P" : "-" }}</td>
  23. </tr>
  24. <tr>
  25. <th><div>이미지</div></th>
  26. <td>
  27. <div v-if="data.file_path" class="onboard--photo-item">
  28. <img :src="getImageUrl(data.file_path)" :alt="data.file_name || data.name" />
  29. </div>
  30. <template v-else>-</template>
  31. </td>
  32. </tr>
  33. <tr>
  34. <th><div>상태</div></th>
  35. <td>
  36. <span :class="['admin--badge', data.status_YN === 'Y' ? 'admin--badge-active' : 'admin--badge-ended']">
  37. {{ data.status_YN === "Y" ? "사용중" : "미사용" }}
  38. </span>
  39. </td>
  40. </tr>
  41. <tr>
  42. <th><div>등록일</div></th>
  43. <td>{{ formatDateTime(data.created_at) }}</td>
  44. </tr>
  45. <tr>
  46. <th><div>최근 수정</div></th>
  47. <td>{{ formatDateTime(data.updated_at) }}</td>
  48. </tr>
  49. </tbody>
  50. </table>
  51. <!-- 버튼 영역 -->
  52. <div class="admin--form-actions">
  53. <button type="button" class="admin--btn" @click="goToList">
  54. ← 목록으로
  55. </button>
  56. <button type="button" class="admin--btn admin--btn-red-border ml--auto" @click="handleDelete">
  57. 삭제
  58. </button>
  59. <button type="button" class="admin--btn admin--btn-red" @click="goToEdit">
  60. 수정
  61. </button>
  62. </div>
  63. <!-- 알림 모달 -->
  64. <AdminAlertModal
  65. v-if="alertModal.show"
  66. :title="alertModal.title"
  67. :message="alertModal.message"
  68. :type="alertModal.type"
  69. @confirm="handleAlertConfirm"
  70. @cancel="handleAlertCancel"
  71. @close="closeAlertModal"
  72. />
  73. </div>
  74. </div>
  75. </template>
  76. <script setup>
  77. import { ref, onMounted } from "vue";
  78. import { useRoute, useRouter } from "vue-router";
  79. import AdminAlertModal from "~/components/admin/AdminAlertModal.vue";
  80. definePageMeta({
  81. layout: "admin",
  82. middleware: ["auth"],
  83. });
  84. const route = useRoute();
  85. const router = useRouter();
  86. const { get, del } = useApi();
  87. const { getImageUrl } = useImage();
  88. const itemId = route.params.id;
  89. const data = ref({
  90. name: "",
  91. type: "",
  92. point: null,
  93. file_name: "",
  94. file_path: "",
  95. status_YN: "Y",
  96. created_at: "",
  97. updated_at: "",
  98. });
  99. // 구분 라벨/뱃지
  100. const typeLabel = (t) => (t === "T" ? "진출권" : t === "P" ? "포인트" : t === "B" ? "뱃지" : "-");
  101. const typeBadgeClass = (t) =>
  102. t === "T" ? "item--ticket" : t === "P" ? "item--point" : t === "B" ? "item--badge" : "";
  103. // 알림 모달
  104. const alertModal = ref({ show: false, title: "알림", message: "", type: "alert", onConfirm: null });
  105. const showAlert = (message, title = "알림") => {
  106. alertModal.value = { show: true, title, message, type: "alert", onConfirm: null };
  107. };
  108. const showConfirm = (message, onConfirm, title = "확인") => {
  109. alertModal.value = { show: true, title, message, type: "confirm", onConfirm };
  110. };
  111. const closeAlertModal = () => { alertModal.value.show = false; };
  112. const handleAlertConfirm = () => {
  113. if (alertModal.value.onConfirm) alertModal.value.onConfirm();
  114. closeAlertModal();
  115. };
  116. const handleAlertCancel = () => closeAlertModal();
  117. // 상세 조회
  118. const loadDetail = async () => {
  119. const { data: res, error } = await get(`/item/${itemId}`);
  120. if (error || !res?.success) {
  121. showAlert(error?.message || res?.message || "조회에 실패했습니다.", "오류");
  122. return;
  123. }
  124. const row = res.data || {};
  125. data.value = {
  126. name: row.name ?? "",
  127. type: row.type ?? "",
  128. point: row.point ?? null,
  129. file_name: row.file_name ?? "",
  130. file_path: row.file_path ?? "",
  131. status_YN: row.status_YN ?? "Y",
  132. created_at: row.created_at ?? "",
  133. updated_at: row.updated_at ?? "",
  134. };
  135. };
  136. // 삭제
  137. const handleDelete = () => {
  138. showConfirm(
  139. `'${data.value.name}' 아이템을 삭제하시겠습니까?`,
  140. async () => {
  141. const { data: res, error } = await del(`/item/${itemId}`);
  142. if (error || !res?.success) {
  143. showAlert(error?.message || res?.message || "삭제에 실패했습니다.", "오류");
  144. } else {
  145. showAlert(res.message || "삭제되었습니다.", "성공");
  146. setTimeout(() => router.push("/site-manager/item/list"), 800);
  147. }
  148. },
  149. "아이템 삭제"
  150. );
  151. };
  152. // 이동
  153. const goToList = () => router.push("/site-manager/item/list");
  154. const goToEdit = () => router.push(`/site-manager/item/edit/${itemId}`);
  155. // 일시 포맷
  156. const formatDateTime = (dateString) => {
  157. if (!dateString) return "-";
  158. const date = new Date(dateString.replace(" ", "T"));
  159. if (isNaN(date.getTime())) return dateString;
  160. return date.toLocaleString("ko-KR", {
  161. year: "numeric",
  162. month: "2-digit",
  163. day: "2-digit",
  164. hour: "2-digit",
  165. minute: "2-digit",
  166. });
  167. };
  168. onMounted(() => {
  169. loadDetail();
  170. });
  171. </script>